<?php
namespace Process;
use Exception;
class FShare
{
    const ROOT ='/dev/shm/';
    /**
     *
     * @var int
     */
    public $len_every_read = 8192;

    protected $dir ;
    protected $file_path ;
    protected $file_handle ;


    /**
     * FileShare constructor.
     * @param $file_path
     * @throws Exception
     */
    public function __construct($file_path)
    {
        #处理文件目录
        if($file_path)
        {
            $file_path = trim($file_path,' /\\');
            $path_info  = preg_split('#[\\\/]+#m', $file_path);
            foreach($path_info as $k=>$v)
            {
                if($v=='..' || $v == '.')
                {
                    unset($path_info[$k]) ;
                }
            }
            $file_path = join('/',$path_info);
            if(!$file_path)
            {
                throw new Exception('无法识别路径');
            }
        }
        else
        {
            throw new Exception('必须传入目录路径');
        }
        $this->file_path = self::ROOT.$file_path;
        $dir = dirname($this->file_path) ;
        if(!is_dir($dir))
        {
            if(!mkdir($dir,0700,true))
            {
                throw new Exception("无法创建目录：{$file_path}");
            }
        }
        $this->file_handle = fopen($this->file_path, 'c+') ;
        if(!is_resource($this->file_handle))
        {
            throw new Exception('无法打开存储文件');
        }
    }

    /**
     * 这只共享值
     * @param $key
     * @param $value
     * @return bool
     */
    public function set ($key, $value)
    {
        $data = $this->lock();
        if($data===false)
        {
            goto end ;
        }
        $data[$key] = $value ;
        $data = $this->write($data);
        if($data !== false )
        {
            $data = true ;
        }
        flock($this->file_handle, LOCK_UN);
        end:
        return $data ;
    }

    /**
     * 获取值
     * @param $key
     * @param bool $all
     * @return bool|mixed|string
     */
    public function get ($key,$all = false )
    {
        $data = $this->lock();
        if($data === false )
        {
            goto end ;
        }
        if(!$all)
        {
            if(empty($data[$key]))
            {
                $data = false ;
            }
            else
            {
                $data = $data[$key];
            }
        }
        flock($this->file_handle, LOCK_UN);
        end :
        return $data ;
    }

    /**
     * 删除数据
     * @param $key
     * @return bool|mixed|string
     */
    public function unset ($key)
    {
        $data = $this->lock();
        if ($data === false) {
            $data = false;
            goto end;
        }
        if (empty($data[$key])) {
            $data = true;
            goto end;
        }
        unset($data[$key]);
        $this->write($data);
        $data = true;
        end:
        flock($this->file_handle, LOCK_UN);
        return $data;
    }

    /**
     * 写入数据
     * @param $data
     * @return bool|int
     */
    protected function write($data)
    {
        ftruncate($this->file_handle, 0);
        rewind($this->file_handle);
        $data = json_encode($data);
        return fwrite($this->file_handle, $data);
    }

    /**
     * 独占获取文件数据
     * @return bool|mixed|string
     */
    protected function lock()
    {
        if(!rewind($this->file_handle))
        {
            $data = false ;
            goto end ;
        }
        if(!flock($this->file_handle, LOCK_EX))
        {
            $data = false;
            goto end ;
        }
        $data = '';
        while (!feof($this->file_handle))
        {
            $data =$data . fread($this->file_handle, $this->len_every_read);
        }
        $data = trim($data);
        $data = json_decode($data,true);
        if(!$data)
        {
           $data = [] ;
           goto end ;
        }
        end:
        return $data;
    }

    /**
     * 删除存储
     * @return bool
     */
    public function remove()
    {
        if(flock($this->file_handle, LOCK_EX))
        {
            $res =(boolean) ftruncate($this->file_handle, 0);
            flock($this->file_handle, LOCK_UN);
            return $res ;
        }
        else
        {
            return false ;
        }
    }

    /**
     * 释放资源
     */
    public function __destruct()
    {
        if(is_resource($this->file_handle))
        {
            fclose($this->file_handle);
        }
    }
}